package implicits

import java.io.File
import com.github.tototoshi.csv._
import implicits.Implicits._
import org.apache.commons.lang3.StringUtils
import shapeless.TypeCase

import scala.collection.SeqMap
import scala.reflect.ClassTag

/**
 * Created by Administrator on 2019/9/12
 */
trait CSVTool {

  implicit class CSVFile(file: File) {

    def csvLines = {
      val reader = CSVReader.open(file)
      val lines = reader.all()
      reader.close()
      lines
    }
  }

  implicit class CSVLines(lines: List[List[String]]) {

    def toFile(file: File) = {
      val writer = CSVWriter.open(file)
      writer.writeAll(lines)
      writer.close()
      file
    }

    def convertHeaderIgnoreCase(map: SeqMap[String, String]) = {
      val newHeaders = lines.head.map { header =>
        map.getIgnoreCaseOrElse(header, header)
      }
      newHeaders +: lines.drop(1)
    }

    def toTxtFile(file: File) = {
      lines.map(_.mkString("\t")).toFile(file)
    }

    def convertHeader(map: collection.Map[String, String]): List[List[String]] = {

      val intTypeCase = TypeCase[Map[Int, String]]
      val strTypeCase = TypeCase[Map[String, String]]
      val newHeaders = map match {
        case intTypeCase(useMap) =>
          lines.head.zipWithIndex.map { case (header, j) =>
            useMap.getOrElse(j, header)
          }
        case strTypeCase(useMap) =>
          lines.head.map { header =>
            useMap.getOrElseIgnoreCase(header, header)
          }
      }
      newHeaders +: lines.drop(1)
    }

    def convertHeader[X: ClassTag](map: collection.Map[Int, String]): List[List[String]] = {

      val intTypeCase = TypeCase[Map[Int, String]]
      val strTypeCase = TypeCase[Map[String, String]]
      val newHeaders = map match {
        case intTypeCase(useMap) =>
          lines.head.zipWithIndex.map { case (header, j) =>
            useMap.getOrElse(j, header)
          }
        case strTypeCase(useMap) =>
          lines.head.map { header =>
            useMap.getOrElseIgnoreCase(header, header)
          }
      }
      newHeaders +: lines.drop(1)
    }

    def lineMap = {
      val headers = lines.head.toLowerCase
      lines.drop(1).map { columns =>
        headers.map(_.toLowerCase).zip(columns).toMap
      }
    }

    def lineMapNoLower = {
      val headers = lines.head
      lines.drop(1).map { tmpColumns =>
        val columns = tmpColumns.padTo(headers.size, "")
        headers.zip(columns).toSeqMap
      }
    }

    def selectOneColumn(columnName: String) = {
      val maps = lineMap
      maps.map { map =>
        map(columnName.toLowerCase)
      }
    }

    def selectOneColumn(columnIndex: Int) = {
      lines.map { columns =>
        columns(columnIndex)
      }
    }

    def mapOtherByColumns[T](f: List[String] => T) = {
      lines.map { columns =>
        f(columns)
      }

    }

    def notEmptyLines = lines.filter(x => x.exists(y => StringUtils.isNotEmpty(y)))

    def selectColumns(columnNames: List[String]) = {
      if(columnNames.isEmpty){
        lines
      }else{
        val maps = lineSeqMapLower
        val newHeaders = lines.head.filter(x => columnNames.toLowerCase.contains(x.toLowerCase))
        newHeaders :: maps.map { map =>
          newHeaders.map { columnName =>
            map(columnName.toLowerCase)
          }
        }
      }
    }

    def reOrder(headers: List[String]) = {
      val newHeaders = headers ::: lines.head.filter(x => !headers.containsIgnoreCase(x))
      val otherLines = lines.lineSeqMapLower.map { map =>
        val newKeys = newHeaders.toLowerCase
        newKeys.map { header =>
          map(header)
        }
      }
      newHeaders :: otherLines
    }

    def lineSeqMapLower = {
      val headers = lines.head.toLowerCase
      lines.drop(1).map { tmpColumns =>
        val columns=tmpColumns.padTo(headers.size, "")
        headers.zip(columns).toSeqMap
      }
    }

    def lineSeqMap = {
      val headers = lines.head
      lines.drop(1).map { columns =>
        headers.zip(columns).toSeqMap
      }
    }

    def removeColumnsCanMore(columnNames: List[String]) = {
      val maps = lineSeqMapLower
      val newHeaders = lines.head.filterNot(x => columnNames.toLowerCase.contains(x.toLowerCase()))
      newHeaders :: maps.map { map =>
        newHeaders.map { columnName =>
          map(columnName.toLowerCase)
        }
      }
    }

  }


}
